<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>The source code</title>
  <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
  <script type="text/javascript" src="../prettify/prettify.js"></script>
  <style type="text/css">
    .highlight { display: block; background-color: #ddd; }
  </style>
  <script type="text/javascript">
    function highlight() {
      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
    }
  </script>
</head>
<body onload="prettyPrint(); highlight();">
  <pre class="prettyprint lang-js"><span id='Ext-form-field-ComboBox'>/**
</span> * @class Ext.form.field.ComboBox
 * @extends Ext.form.field.Picker
 *
 * A combobox control with support for autocomplete, remote loading, and many other features.
 *
 * A ComboBox is like a combination of a traditional HTML text `&lt;input&gt;` field and a `&lt;select&gt;`
 * field; the user is able to type freely into the field, and/or pick values from a dropdown selection
 * list. The user can input any value by default, even if it does not appear in the selection list;
 * to prevent free-form values and restrict them to items in the list, set {@link #forceSelection} to `true`.
 *
 * The selection list's options are populated from any {@link Ext.data.Store}, including remote
 * stores. The data items in the store are mapped to each option's displayed text and backing value via
 * the {@link #valueField} and {@link #displayField} configurations, respectively.
 *
 * If your store is not remote, i.e. it depends only on local data and is loaded up front, you should be
 * sure to set the {@link #queryMode} to `'local'`, as this will improve responsiveness for the user.
 *
 * {@img Ext.form.ComboBox/Ext.form.ComboBox.png Ext.form.ComboBox component}
 *
 * ## Example usage:
 *
 *     // The data store containing the list of states
 *     var states = Ext.create('Ext.data.Store', {
 *         fields: ['abbr', 'name'],
 *         data : [
 *             {&quot;abbr&quot;:&quot;AL&quot;, &quot;name&quot;:&quot;Alabama&quot;},
 *             {&quot;abbr&quot;:&quot;AK&quot;, &quot;name&quot;:&quot;Alaska&quot;},
 *             {&quot;abbr&quot;:&quot;AZ&quot;, &quot;name&quot;:&quot;Arizona&quot;}
 *             //...
 *         ]
 *     });
 *
 *     // Create the combo box, attached to the states data store
 *     Ext.create('Ext.form.ComboBox', {
 *         fieldLabel: 'Choose State',
 *         store: states,
 *         queryMode: 'local',
 *         displayField: 'name',
 *         valueField: 'abbr',
 *         renderTo: Ext.getBody()
 *     });
 *
 * ## Events
 *
 * To do something when something in ComboBox is selected, configure the select event:
 *
 *     var cb = Ext.create('Ext.form.ComboBox', {
 *         // all of your config options
 *         listeners:{
 *              scope: yourScope,
 *              'select': yourFunction
 *         }
 *     });
 *
 *     // Alternatively, you can assign events after the object is created:
 *     var cb = new Ext.form.field.ComboBox(yourOptions);
 *     cb.on('select', yourFunction, yourScope);
 *
 * ## Multiple Selection
 *
 * ComboBox also allows selection of multiple items from the list; to enable multi-selection set the
 * {@link #multiSelect} config to `true`.
 *
 * @docauthor Jason Johnston &lt;jason@sencha.com&gt;
 */
Ext.define('Ext.form.field.ComboBox', {
    extend:'Ext.form.field.Picker',
    requires: ['Ext.util.DelayedTask', 'Ext.EventObject', 'Ext.view.BoundList', 'Ext.view.BoundListKeyNav', 'Ext.data.StoreManager'],
    alternateClassName: 'Ext.form.ComboBox',
    alias: ['widget.combobox', 'widget.combo'],

<span id='Ext-form-field-ComboBox-cfg-triggerCls'>    /**
</span>     * @cfg {String} triggerCls
     * An additional CSS class used to style the trigger button. The trigger will always get the
     * {@link #triggerBaseCls} by default and &lt;code&gt;triggerCls&lt;/code&gt; will be &lt;b&gt;appended&lt;/b&gt; if specified.
     * Defaults to 'x-form-arrow-trigger' for ComboBox.
     */
    triggerCls: Ext.baseCSSPrefix + 'form-arrow-trigger',

<span id='Ext-form-field-ComboBox-cfg-store'>    /**
</span>     * @cfg {Ext.data.Store/Array} store The data source to which this combo is bound (defaults to &lt;code&gt;undefined&lt;/code&gt;).
     * Acceptable values for this property are:
     * &lt;div class=&quot;mdetail-params&quot;&gt;&lt;ul&gt;
     * &lt;li&gt;&lt;b&gt;any {@link Ext.data.Store Store} subclass&lt;/b&gt;&lt;/li&gt;
     * &lt;li&gt;&lt;b&gt;an Array&lt;/b&gt; : Arrays will be converted to a {@link Ext.data.Store} internally,
     * automatically generating {@link Ext.data.Field#name field names} to work with all data components.
     * &lt;div class=&quot;mdetail-params&quot;&gt;&lt;ul&gt;
     * &lt;li&gt;&lt;b&gt;1-dimensional array&lt;/b&gt; : (e.g., &lt;code&gt;['Foo','Bar']&lt;/code&gt;)&lt;div class=&quot;sub-desc&quot;&gt;
     * A 1-dimensional array will automatically be expanded (each array item will be used for both the combo
     * {@link #valueField} and {@link #displayField})&lt;/div&gt;&lt;/li&gt;
     * &lt;li&gt;&lt;b&gt;2-dimensional array&lt;/b&gt; : (e.g., &lt;code&gt;[['f','Foo'],['b','Bar']]&lt;/code&gt;)&lt;div class=&quot;sub-desc&quot;&gt;
     * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo
     * {@link #valueField}, while the value at index 1 is assumed to be the combo {@link #displayField}.
     * &lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
     * &lt;p&gt;See also &lt;code&gt;{@link #queryMode}&lt;/code&gt;.&lt;/p&gt;
     */

<span id='Ext-form-field-ComboBox-cfg-multiSelect'>    /**
</span>     * @cfg {Boolean} multiSelect
     * If set to &lt;code&gt;true&lt;/code&gt;, allows the combo field to hold more than one value at a time, and allows selecting
     * multiple items from the dropdown list. The combo's text field will show all selected values separated by
     * the {@link #delimiter}. (Defaults to &lt;code&gt;false&lt;/code&gt;.)
     */
    multiSelect: false,

<span id='Ext-form-field-ComboBox-cfg-delimiter'>    /**
</span>     * @cfg {String} delimiter
     * The character(s) used to separate the {@link #displayField display values} of multiple selected items
     * when &lt;code&gt;{@link #multiSelect} = true&lt;/code&gt;. Defaults to &lt;code&gt;', '&lt;/code&gt;.
     */
    delimiter: ', ',

<span id='Ext-form-field-ComboBox-cfg-displayField'>    /**
</span>     * @cfg {String} displayField The underlying {@link Ext.data.Field#name data field name} to bind to this
     * ComboBox (defaults to 'text').
     * &lt;p&gt;See also &lt;code&gt;{@link #valueField}&lt;/code&gt;.&lt;/p&gt;
     */
    displayField: 'text',

<span id='Ext-form-field-ComboBox-cfg-valueField'>    /**
</span>     * @cfg {String} valueField
     * @required
     * The underlying {@link Ext.data.Field#name data value name} to bind to this ComboBox (defaults to match
     * the value of the {@link #displayField} config).
     * &lt;p&gt;&lt;b&gt;Note&lt;/b&gt;: use of a &lt;code&gt;valueField&lt;/code&gt; requires the user to make a selection in order for a value to be
     * mapped. See also &lt;code&gt;{@link #displayField}&lt;/code&gt;.&lt;/p&gt;
     */

<span id='Ext-form-field-ComboBox-cfg-triggerAction'>    /**
</span>     * @cfg {String} triggerAction The action to execute when the trigger is clicked.
     * &lt;div class=&quot;mdetail-params&quot;&gt;&lt;ul&gt;
     * &lt;li&gt;&lt;b&gt;&lt;code&gt;'all'&lt;/code&gt;&lt;/b&gt; : &lt;b&gt;Default&lt;/b&gt;
     * &lt;p class=&quot;sub-desc&quot;&gt;{@link #doQuery run the query} specified by the &lt;code&gt;{@link #allQuery}&lt;/code&gt; config option&lt;/p&gt;&lt;/li&gt;
     * &lt;li&gt;&lt;b&gt;&lt;code&gt;'query'&lt;/code&gt;&lt;/b&gt; :
     * &lt;p class=&quot;sub-desc&quot;&gt;{@link #doQuery run the query} using the {@link Ext.form.field.Base#getRawValue raw value}.&lt;/p&gt;&lt;/li&gt;
     * &lt;/ul&gt;&lt;/div&gt;
     * &lt;p&gt;See also &lt;code&gt;{@link #queryParam}&lt;/code&gt;.&lt;/p&gt;
     */
    triggerAction: 'all',

<span id='Ext-form-field-ComboBox-cfg-allQuery'>    /**
</span>     * @cfg {String} allQuery The text query to send to the server to return all records for the list
     * with no filtering (defaults to '')
     */
    allQuery: '',

<span id='Ext-form-field-ComboBox-cfg-queryParam'>    /**
</span>     * @cfg {String} queryParam Name of the parameter used by the Store to pass the typed string when the ComboBox is configured with
     * &lt;code&gt;{@link #queryMode}: 'remote'&lt;/code&gt; (defaults to &lt;code&gt;'query'&lt;/code&gt;). If explicitly set to a falsy value it will
     * not be sent.
     */
    queryParam: 'query',

<span id='Ext-form-field-ComboBox-cfg-queryMode'>    /**
</span>     * @cfg {String} queryMode
     * The mode in which the ComboBox uses the configured Store. Acceptable values are:
     * &lt;div class=&quot;mdetail-params&quot;&gt;&lt;ul&gt;
     * &lt;li&gt;&lt;b&gt;&lt;code&gt;'remote'&lt;/code&gt;&lt;/b&gt; : &lt;b&gt;Default&lt;/b&gt;
     * &lt;p&gt;In &lt;code&gt;queryMode: 'remote'&lt;/code&gt;, the ComboBox loads its Store dynamically based upon user interaction.&lt;/p&gt;
     * &lt;p&gt;This is typically used for &quot;autocomplete&quot; type inputs, and after the user finishes typing, the Store is {@link Ext.data.Store#load load}ed.&lt;/p&gt;
     * &lt;p&gt;A parameter containing the typed string is sent in the load request. The default parameter name for the input string is &lt;code&gt;query&lt;/code&gt;, but this
     * can be configured using the {@link #queryParam} config.&lt;/p&gt;
     * &lt;p&gt;In &lt;code&gt;queryMode: 'remote'&lt;/code&gt;, the Store may be configured with &lt;code&gt;{@link Ext.data.Store#remoteFilter remoteFilter}: true&lt;/code&gt;,
     * and further filters may be &lt;i&gt;programatically&lt;/i&gt; added to the Store which are then passed with every load request which allows the server
     * to further refine the returned dataset.&lt;/p&gt;
     * &lt;p&gt;Typically, in an autocomplete situation, {@link #hideTrigger} is configured &lt;code&gt;true&lt;/code&gt; because it has no meaning for autocomplete.&lt;/p&gt;&lt;/li&gt;
     * &lt;li&gt;&lt;b&gt;&lt;code&gt;'local'&lt;/code&gt;&lt;/b&gt; :
     * &lt;p class=&quot;sub-desc&quot;&gt;ComboBox loads local data&lt;/p&gt;
     * &lt;pre&gt;&lt;code&gt;
var combo = new Ext.form.field.ComboBox({
    renderTo: document.body,
    queryMode: 'local',
    store: new Ext.data.ArrayStore({
        id: 0,
        fields: [
            'myId',  // numeric value is the key
            'displayText'
        ],
        data: [[1, 'item1'], [2, 'item2']]  // data is local
    }),
    valueField: 'myId',
    displayField: 'displayText',
    triggerAction: 'all'
});
     * &lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
     * &lt;/ul&gt;&lt;/div&gt;
     */
    queryMode: 'remote',

    queryCaching: true,

<span id='Ext-form-field-ComboBox-cfg-pageSize'>    /**
</span>     * @cfg {Number} pageSize If greater than &lt;code&gt;0&lt;/code&gt;, a {@link Ext.toolbar.Paging} is displayed in the
     * footer of the dropdown list and the {@link #doQuery filter queries} will execute with page start and
     * {@link Ext.toolbar.Paging#pageSize limit} parameters. Only applies when &lt;code&gt;{@link #queryMode} = 'remote'&lt;/code&gt;
     * (defaults to &lt;code&gt;0&lt;/code&gt;).
     */
    pageSize: 0,

<span id='Ext-form-field-ComboBox-cfg-queryDelay'>    /**
</span>     * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and
     * sending the query to filter the dropdown list (defaults to &lt;code&gt;500&lt;/code&gt; if &lt;code&gt;{@link #queryMode} = 'remote'&lt;/code&gt;
     * or &lt;code&gt;10&lt;/code&gt; if &lt;code&gt;{@link #queryMode} = 'local'&lt;/code&gt;)
     */

<span id='Ext-form-field-ComboBox-cfg-minChars'>    /**
</span>     * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and
     * {@link #typeAhead} activate (defaults to &lt;code&gt;4&lt;/code&gt; if &lt;code&gt;{@link #queryMode} = 'remote'&lt;/code&gt; or &lt;code&gt;0&lt;/code&gt; if
     * &lt;code&gt;{@link #queryMode} = 'local'&lt;/code&gt;, does not apply if &lt;code&gt;{@link Ext.form.field.Trigger#editable editable} = false&lt;/code&gt;).
     */

<span id='Ext-form-field-ComboBox-cfg-autoSelect'>    /**
</span>     * @cfg {Boolean} autoSelect &lt;code&gt;true&lt;/code&gt; to automatically highlight the first result gathered by the data store
     * in the dropdown list when it is opened. (Defaults to &lt;code&gt;true&lt;/code&gt;). A false value would cause nothing in the
     * list to be highlighted automatically, so the user would have to manually highlight an item before pressing
     * the enter or {@link #selectOnTab tab} key to select it (unless the value of ({@link #typeAhead}) were true),
     * or use the mouse to select a value.
     */
    autoSelect: true,

<span id='Ext-form-field-ComboBox-cfg-typeAhead'>    /**
</span>     * @cfg {Boolean} typeAhead &lt;code&gt;true&lt;/code&gt; to populate and autoselect the remainder of the text being
     * typed after a configurable delay ({@link #typeAheadDelay}) if it matches a known value (defaults
     * to &lt;code&gt;false&lt;/code&gt;)
     */
    typeAhead: false,

<span id='Ext-form-field-ComboBox-cfg-typeAheadDelay'>    /**
</span>     * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
     * if &lt;code&gt;{@link #typeAhead} = true&lt;/code&gt; (defaults to &lt;code&gt;250&lt;/code&gt;)
     */
    typeAheadDelay: 250,

<span id='Ext-form-field-ComboBox-cfg-selectOnTab'>    /**
</span>     * @cfg {Boolean} selectOnTab
     * Whether the Tab key should select the currently highlighted item. Defaults to &lt;code&gt;true&lt;/code&gt;.
     */
    selectOnTab: true,

<span id='Ext-form-field-ComboBox-cfg-forceSelection'>    /**
</span>     * @cfg {Boolean} forceSelection &lt;code&gt;true&lt;/code&gt; to restrict the selected value to one of the values in the list,
     * &lt;code&gt;false&lt;/code&gt; to allow the user to set arbitrary text into the field (defaults to &lt;code&gt;false&lt;/code&gt;)
     */
    forceSelection: false,

<span id='Ext-form-field-ComboBox-cfg-valueNotFoundText'>    /**
</span>     * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
     * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined). If this
     * default text is used, it means there is no value set and no validation will occur on this field.
     */

<span id='Ext-form-field-ComboBox-property-lastQuery'>    /**
</span>     * The value of the match string used to filter the store. Delete this property to force a requery.
     * Example use:
     * &lt;pre&gt;&lt;code&gt;
var combo = new Ext.form.field.ComboBox({
    ...
    queryMode: 'remote',
    listeners: {
        // delete the previous query in the beforequery event or set
        // combo.lastQuery = null (this will reload the store the next time it expands)
        beforequery: function(qe){
            delete qe.combo.lastQuery;
        }
    }
});
     * &lt;/code&gt;&lt;/pre&gt;
     * To make sure the filter in the store is not cleared the first time the ComboBox trigger is used
     * configure the combo with &lt;code&gt;lastQuery=''&lt;/code&gt;. Example use:
     * &lt;pre&gt;&lt;code&gt;
var combo = new Ext.form.field.ComboBox({
    ...
    queryMode: 'local',
    triggerAction: 'all',
    lastQuery: ''
});
     * &lt;/code&gt;&lt;/pre&gt;
     * @property lastQuery
     * @type String
     */

<span id='Ext-form-field-ComboBox-cfg-defaultListConfig'>    /**
</span>     * @cfg {Object} defaultListConfig
     * Set of options that will be used as defaults for the user-configured {@link #listConfig} object.
     */
    defaultListConfig: {
        emptyText: '',
        loadingText: 'Loading...',
        loadingHeight: 70,
        minWidth: 70,
        maxHeight: 300,
        shadow: 'sides'
    },

<span id='Ext-form-field-ComboBox-cfg-transform'>    /**
</span>     * @cfg {Mixed} transform
     * The id, DOM node or {@link Ext.core.Element} of an existing HTML &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element to
     * convert into a ComboBox. The target select's options will be used to build the options in the ComboBox
     * dropdown; a configured {@link #store} will take precedence over this.
     */

<span id='Ext-form-field-ComboBox-cfg-listConfig'>    /**
</span>     * @cfg {Object} listConfig
     * &lt;p&gt;An optional set of configuration properties that will be passed to the {@link Ext.view.BoundList}'s
     * constructor. Any configuration that is valid for BoundList can be included. Some of the more useful
     * ones are:&lt;/p&gt;
     * &lt;ul&gt;
     *     &lt;li&gt;{@link Ext.view.BoundList#cls} - defaults to empty&lt;/li&gt;
     *     &lt;li&gt;{@link Ext.view.BoundList#emptyText} - defaults to empty string&lt;/li&gt;
     *     &lt;li&gt;{@link Ext.view.BoundList#getInnerTpl} - defaults to the template defined in BoundList&lt;/li&gt;
     *     &lt;li&gt;{@link Ext.view.BoundList#itemSelector} - defaults to the value defined in BoundList&lt;/li&gt;
     *     &lt;li&gt;{@link Ext.view.BoundList#loadingText} - defaults to &lt;code&gt;'Loading...'&lt;/code&gt;&lt;/li&gt;
     *     &lt;li&gt;{@link Ext.view.BoundList#minWidth} - defaults to &lt;code&gt;70&lt;/code&gt;&lt;/li&gt;
     *     &lt;li&gt;{@link Ext.view.BoundList#maxWidth} - defaults to &lt;code&gt;undefined&lt;/code&gt;&lt;/li&gt;
     *     &lt;li&gt;{@link Ext.view.BoundList#maxHeight} - defaults to &lt;code&gt;300&lt;/code&gt;&lt;/li&gt;
     *     &lt;li&gt;{@link Ext.view.BoundList#resizable} - defaults to &lt;code&gt;false&lt;/code&gt;&lt;/li&gt;
     *     &lt;li&gt;{@link Ext.view.BoundList#shadow} - defaults to &lt;code&gt;'sides'&lt;/code&gt;&lt;/li&gt;
     *     &lt;li&gt;{@link Ext.view.BoundList#width} - defaults to &lt;code&gt;undefined&lt;/code&gt; (automatically set to the width
     *         of the ComboBox field if {@link #matchFieldWidth} is true)&lt;/li&gt;
     * &lt;/ul&gt;
     */

    //private
    ignoreSelection: 0,

    initComponent: function() {
        var me = this,
            isDefined = Ext.isDefined,
            store = me.store,
            transform = me.transform,
            transformSelect, isLocalMode;

        //&lt;debug&gt;
        if (!store &amp;&amp; !transform) {
            Ext.Error.raise('Either a valid store, or a HTML select to transform, must be configured on the combo.');
        }
        if (me.typeAhead &amp;&amp; me.multiSelect) {
            Ext.Error.raise('typeAhead and multiSelect are mutually exclusive options -- please remove one of them.');
        }
        if (me.typeAhead &amp;&amp; !me.editable) {
            Ext.Error.raise('If typeAhead is enabled the combo must be editable: true -- please change one of those settings.');
        }
        if (me.selectOnFocus &amp;&amp; !me.editable) {
            Ext.Error.raise('If selectOnFocus is enabled the combo must be editable: true -- please change one of those settings.');
        }
        //&lt;/debug&gt;

        this.addEvents(
            // TODO need beforeselect?

<span id='Ext-form-field-ComboBox-event-beforequery'>            /**
</span>             * @event beforequery
             * Fires before all queries are processed. Return false to cancel the query or set the queryEvent's
             * cancel property to true.
             * @param {Object} queryEvent An object that has these properties:&lt;ul&gt;
             * &lt;li&gt;&lt;code&gt;combo&lt;/code&gt; : Ext.form.field.ComboBox &lt;div class=&quot;sub-desc&quot;&gt;This combo box&lt;/div&gt;&lt;/li&gt;
             * &lt;li&gt;&lt;code&gt;query&lt;/code&gt; : String &lt;div class=&quot;sub-desc&quot;&gt;The query string&lt;/div&gt;&lt;/li&gt;
             * &lt;li&gt;&lt;code&gt;forceAll&lt;/code&gt; : Boolean &lt;div class=&quot;sub-desc&quot;&gt;True to force &quot;all&quot; query&lt;/div&gt;&lt;/li&gt;
             * &lt;li&gt;&lt;code&gt;cancel&lt;/code&gt; : Boolean &lt;div class=&quot;sub-desc&quot;&gt;Set to true to cancel the query&lt;/div&gt;&lt;/li&gt;
             * &lt;/ul&gt;
             */
            'beforequery',

            /*
             * @event select
             * Fires when at least one list item is selected.
             * @param {Ext.form.field.ComboBox} combo This combo box
             * @param {Array} records The selected records
             */
            'select'
        );

        // Build store from 'transform' HTML select element's options
        if (!store &amp;&amp; transform) {
            transformSelect = Ext.getDom(transform);
            if (transformSelect) {
                store = Ext.Array.map(Ext.Array.from(transformSelect.options), function(option) {
                    return [option.value, option.text];
                });
                if (!me.name) {
                    me.name = transformSelect.name;
                }
                if (!('value' in me)) {
                    me.value = transformSelect.value;
                }
            }
        }

        me.bindStore(store, true);
        store = me.store;
        if (store.autoCreated) {
            me.queryMode = 'local';
            me.valueField = me.displayField = 'field1';
            if (!store.expanded) {
                me.displayField = 'field2';
            }
        }


        if (!isDefined(me.valueField)) {
            me.valueField = me.displayField;
        }

        isLocalMode = me.queryMode === 'local';
        if (!isDefined(me.queryDelay)) {
            me.queryDelay = isLocalMode ? 10 : 500;
        }
        if (!isDefined(me.minChars)) {
            me.minChars = isLocalMode ? 0 : 4;
        }

        if (!me.displayTpl) {
            me.displayTpl = Ext.create('Ext.XTemplate',
                '&lt;tpl for=&quot;.&quot;&gt;' +
                    '{[typeof values === &quot;string&quot; ? values : values.' + me.displayField + ']}' +
                    '&lt;tpl if=&quot;xindex &lt; xcount&quot;&gt;' + me.delimiter + '&lt;/tpl&gt;' +
                '&lt;/tpl&gt;'
            );
        } else if (Ext.isString(me.displayTpl)) {
            me.displayTpl = Ext.create('Ext.XTemplate', me.displayTpl);
        }

        me.callParent();

        me.doQueryTask = Ext.create('Ext.util.DelayedTask', me.doRawQuery, me);

        // store has already been loaded, setValue
        if (me.store.getCount() &gt; 0) {
            me.setValue(me.value);
        }

        // render in place of 'transform' select
        if (transformSelect) {
            me.render(transformSelect.parentNode, transformSelect);
            Ext.removeNode(transformSelect);
            delete me.renderTo;
        }
    },

    beforeBlur: function() {
        var me = this;
        me.doQueryTask.cancel();
        if (me.forceSelection) {
            me.assertValue();
        } else {
            me.collapse();
        }
    },

    // private
    assertValue: function() {
        var me = this,
            value = me.getRawValue(),
            rec;

        if (me.multiSelect) {
            // For multiselect, check that the current displayed value matches the current
            // selection, if it does not then revert to the most recent selection.
            if (value !== me.getDisplayValue()) {
                me.setValue(me.lastSelection);
            }
        } else {
            // For single-select, match the displayed value to a record and select it,
            // if it does not match a record then revert to the most recent selection.
            rec = me.findRecordByDisplay(value);
            if (rec) {
                me.select(rec);
            } else {
                me.setValue(me.lastSelection);
            }
        }
        me.collapse();
    },

    onTypeAhead: function() {
        var me = this,
            displayField = me.displayField,
            record = me.store.findRecord(displayField, me.getRawValue()),
            boundList = me.getPicker(),
            newValue, len, selStart;

        if (record) {
            newValue = record.get(displayField);
            len = newValue.length;
            selStart = me.getRawValue().length;

            boundList.highlightItem(boundList.getNode(record));

            if (selStart !== 0 &amp;&amp; selStart !== len) {
                me.setRawValue(newValue);
                me.selectText(selStart, newValue.length);
            }
        }
    },

    // invoked when a different store is bound to this combo
    // than the original
    resetToDefault: function() {

    },

    bindStore: function(store, initial) {
        var me = this,
            oldStore = me.store;

        // this code directly accesses this.picker, bc invoking getPicker
        // would create it when we may be preping to destroy it
        if (oldStore &amp;&amp; !initial) {
            if (oldStore !== store &amp;&amp; oldStore.autoDestroy) {
                oldStore.destroy();
            } else {
                oldStore.un({
                    scope: me,
                    load: me.onLoad,
                    exception: me.collapse
                });
            }
            if (!store) {
                me.store = null;
                if (me.picker) {
                    me.picker.bindStore(null);
                }
            }
        }
        if (store) {
            if (!initial) {
                me.resetToDefault();
            }

            me.store = Ext.data.StoreManager.lookup(store);
            me.store.on({
                scope: me,
                load: me.onLoad,
                exception: me.collapse
            });

            if (me.picker) {
                me.picker.bindStore(store);
            }
        }
    },

    onLoad: function() {
        var me = this,
            value = me.value;

        me.syncSelection();
        if (me.picker &amp;&amp; !me.picker.getSelectionModel().hasSelection()) {
            me.doAutoSelect();
        }
    },

<span id='Ext-form-field-ComboBox-method-doRawQuery'>    /**
</span>     * @private
     * Execute the query with the raw contents within the textfield.
     */
    doRawQuery: function() {
        this.doQuery(this.getRawValue());
    },

<span id='Ext-form-field-ComboBox-method-doQuery'>    /**
</span>     * Executes a query to filter the dropdown list. Fires the {@link #beforequery} event prior to performing the
     * query allowing the query action to be canceled if needed.
     * @param {String} queryString The SQL query to execute
     * @param {Boolean} forceAll &lt;code&gt;true&lt;/code&gt; to force the query to execute even if there are currently fewer
     * characters in the field than the minimum specified by the &lt;code&gt;{@link #minChars}&lt;/code&gt; config option.  It
     * also clears any filter previously saved in the current store (defaults to &lt;code&gt;false&lt;/code&gt;)
     * @return {Boolean} true if the query was permitted to run, false if it was cancelled by a {@link #beforequery} handler.
     */
    doQuery: function(queryString, forceAll) {
        queryString = queryString || '';

        // store in object and pass by reference in 'beforequery'
        // so that client code can modify values.
        var me = this,
            qe = {
                query: queryString,
                forceAll: forceAll,
                combo: me,
                cancel: false
            },
            store = me.store,
            isLocalMode = me.queryMode === 'local';

        if (me.fireEvent('beforequery', qe) === false || qe.cancel) {
            return false;
        }

        // get back out possibly modified values
        queryString = qe.query;
        forceAll = qe.forceAll;

        // query permitted to run
        if (forceAll || (queryString.length &gt;= me.minChars)) {
            // expand before starting query so LoadMask can position itself correctly
            me.expand();

            // make sure they aren't querying the same thing
            if (!me.queryCaching || me.lastQuery !== queryString) {
                me.lastQuery = queryString;

                if (isLocalMode) {
                    // forceAll means no filtering - show whole dataset.
                    if (forceAll) {
                        store.clearFilter();
                    } else {
                        // Clear filter, but supress event so that the BoundList is not immediately updated.
                        store.clearFilter(true);
                        store.filter(me.displayField, queryString);
                    }
                } else {
                    // In queryMode: 'remote', we assume Store filters are added by the developer as remote filters,
                    // and these are automatically passed as params with every load call, so we do *not* call clearFilter.
                    store.load({
                        params: me.getParams(queryString)
                    });
                }
            }

            // Clear current selection if it does not match the current value in the field
            if (me.getRawValue() !== me.getDisplayValue()) {
                me.ignoreSelection++;
                me.picker.getSelectionModel().deselectAll();
                me.ignoreSelection--;
            }

            if (isLocalMode) {
                me.doAutoSelect();
            }
            if (me.typeAhead) {
                me.doTypeAhead();
            }
        }
        return true;
    },

    // private
    getParams: function(queryString) {
        var p = {},
            pageSize = this.pageSize,
            param = this.queryParam;

        if (param) {
            p[param] = queryString;
        }

        if (pageSize) {
            p.start = 0;
            p.limit = pageSize;
        }
        return p;
    },

<span id='Ext-form-field-ComboBox-method-doAutoSelect'>    /**
</span>     * @private
     * If the autoSelect config is true, and the picker is open, highlights the first item.
     */
    doAutoSelect: function() {
        var me = this,
            picker = me.picker,
            lastSelected, itemNode;
        if (picker &amp;&amp; me.autoSelect &amp;&amp; me.store.getCount() &gt; 0) {
            // Highlight the last selected item and scroll it into view
            lastSelected = picker.getSelectionModel().lastSelected;
            itemNode = picker.getNode(lastSelected || 0);
            if (itemNode) {
                picker.highlightItem(itemNode);
                picker.listEl.scrollChildIntoView(itemNode, false);
            }
        }
    },

    doTypeAhead: function() {
        if (!this.typeAheadTask) {
            this.typeAheadTask = Ext.create('Ext.util.DelayedTask', this.onTypeAhead, this);
        }
        if (this.lastKey != Ext.EventObject.BACKSPACE &amp;&amp; this.lastKey != Ext.EventObject.DELETE) {
            this.typeAheadTask.delay(this.typeAheadDelay);
        }
    },

    onTriggerClick: function() {
        var me = this;
        if (!me.readOnly &amp;&amp; !me.disabled) {
            if (me.isExpanded) {
                me.collapse();
            } else {
                me.onFocus({});
                if (me.triggerAction === 'all') {
                    me.doQuery(me.allQuery, true);
                } else {
                    me.doQuery(me.getRawValue());
                }
            }
            me.inputEl.focus();
        }
    },


    // store the last key and doQuery if relevant
    onKeyUp: function(e, t) {
        var me = this,
            key = e.getKey();

        if (!me.readOnly &amp;&amp; !me.disabled &amp;&amp; me.editable) {
            me.lastKey = key;
            // we put this in a task so that we can cancel it if a user is
            // in and out before the queryDelay elapses

            // perform query w/ any normal key or backspace or delete
            if (!e.isSpecialKey() || key == e.BACKSPACE || key == e.DELETE) {
                me.doQueryTask.delay(me.queryDelay);
            }
        }

        if (me.enableKeyEvents) {
            me.callParent(arguments);
        }
    },

    initEvents: function() {
        var me = this;
        me.callParent();

        /*
         * Setup keyboard handling. If enableKeyEvents is true, we already have
         * a listener on the inputEl for keyup, so don't create a second.
         */
        if (!me.enableKeyEvents) {
            me.mon(me.inputEl, 'keyup', me.onKeyUp, me);
        }
    },

    createPicker: function() {
        var me = this,
            picker,
            menuCls = Ext.baseCSSPrefix + 'menu',
            opts = Ext.apply({
                selModel: {
                    mode: me.multiSelect ? 'SIMPLE' : 'SINGLE'
                },
                floating: true,
                hidden: true,
                ownerCt: me.ownerCt,
                cls: me.el.up('.' + menuCls) ? menuCls : '',
                store: me.store,
                displayField: me.displayField,
                focusOnToFront: false,
                pageSize: me.pageSize,
                tpl: me.tpl
            }, me.listConfig, me.defaultListConfig);

        picker = me.picker = Ext.create('Ext.view.BoundList', opts);

        me.mon(picker, {
            itemclick: me.onItemClick,
            refresh: me.onListRefresh,
            scope: me
        });

        me.mon(picker.getSelectionModel(), 'selectionchange', me.onListSelectionChange, me);

        return picker;
    },

    onListRefresh: function() {
        this.alignPicker();
        this.syncSelection();
    },

    onItemClick: function(picker, record){
        /*
         * If we're doing single selection, the selection change events won't fire when
         * clicking on the selected element. Detect it here.
         */
        var me = this,
            lastSelection = me.lastSelection,
            valueField = me.valueField,
            selected;

        if (!me.multiSelect &amp;&amp; lastSelection) {
            selected = lastSelection[0];
            if (selected &amp;&amp; (record.get(valueField) === selected.get(valueField))) {
                me.collapse();
            }
        }
    },

    onListSelectionChange: function(list, selectedRecords) {
        var me = this,
            isMulti = me.multiSelect,
            hasRecords = selectedRecords.length &gt; 0;
        // Only react to selection if it is not called from setValue, and if our list is
        // expanded (ignores changes to the selection model triggered elsewhere)
        if (!me.ignoreSelection &amp;&amp; me.isExpanded) {
            if (!isMulti) {
                Ext.defer(me.collapse, 1, me);
            }
            /*
             * Only set the value here if we're in multi selection mode or we have
             * a selection. Otherwise setValue will be called with an empty value
             * which will cause the change event to fire twice.
             */
            if (isMulti || hasRecords) {
                me.setValue(selectedRecords, false);
            }
            if (hasRecords) {
                me.fireEvent('select', me, selectedRecords);
            }
            me.inputEl.focus();
        }
    },

<span id='Ext-form-field-ComboBox-method-onExpand'>    /**
</span>     * @private
     * Enables the key nav for the BoundList when it is expanded.
     */
    onExpand: function() {
        var me = this,
            keyNav = me.listKeyNav,
            selectOnTab = me.selectOnTab,
            picker = me.getPicker();

        // Handle BoundList navigation from the input field. Insert a tab listener specially to enable selectOnTab.
        if (keyNav) {
            keyNav.enable();
        } else {
            keyNav = me.listKeyNav = Ext.create('Ext.view.BoundListKeyNav', this.inputEl, {
                boundList: picker,
                forceKeyDown: true,
                tab: function(e) {
                    if (selectOnTab) {
                        this.selectHighlighted(e);
                        me.triggerBlur();
                    }
                    // Tab key event is allowed to propagate to field
                    return true;
                }
            });
        }

        // While list is expanded, stop tab monitoring from Ext.form.field.Trigger so it doesn't short-circuit selectOnTab
        if (selectOnTab) {
            me.ignoreMonitorTab = true;
        }

        Ext.defer(keyNav.enable, 1, keyNav); //wait a bit so it doesn't react to the down arrow opening the picker
        me.inputEl.focus();
    },

<span id='Ext-form-field-ComboBox-method-onCollapse'>    /**
</span>     * @private
     * Disables the key nav for the BoundList when it is collapsed.
     */
    onCollapse: function() {
        var me = this,
            keyNav = me.listKeyNav;
        if (keyNav) {
            keyNav.disable();
            me.ignoreMonitorTab = false;
        }
    },

<span id='Ext-form-field-ComboBox-method-select'>    /**
</span>     * Selects an item by a {@link Ext.data.Model Model}, or by a key value.
     * @param r
     */
    select: function(r) {
        this.setValue(r, true);
    },

<span id='Ext-form-field-ComboBox-method-findRecord'>    /**
</span>     * Find the record by searching for a specific field/value combination
     * Returns an Ext.data.Record or false
     * @private
     */
    findRecord: function(field, value) {
        var ds = this.store,
            idx = ds.findExact(field, value);
        return idx !== -1 ? ds.getAt(idx) : false;
    },
    findRecordByValue: function(value) {
        return this.findRecord(this.valueField, value);
    },
    findRecordByDisplay: function(value) {
        return this.findRecord(this.displayField, value);
    },

<span id='Ext-form-field-ComboBox-method-setValue'>    /**
</span>     * Sets the specified value(s) into the field. For each value, if a record is found in the {@link #store} that
     * matches based on the {@link #valueField}, then that record's {@link #displayField} will be displayed in the
     * field.  If no match is found, and the {@link #valueNotFoundText} config option is defined, then that will be
     * displayed as the default field text. Otherwise a blank value will be shown, although the value will still be set.
     * @param {String|Array} value The value(s) to be set. Can be either a single String or {@link Ext.data.Model},
     * or an Array of Strings or Models.
     * @return {Ext.form.field.Field} this
     */
    setValue: function(value, doSelect) {
        var me = this,
            valueNotFoundText = me.valueNotFoundText,
            inputEl = me.inputEl,
            i, len, record,
            models = [],
            displayTplData = [],
            processedValue = [];

        if (me.store.loading) {
            // Called while the Store is loading. Ensure it is processed by the onLoad method.
            me.value = value;
            return me;
        }

        // This method processes multi-values, so ensure value is an array.
        value = Ext.Array.from(value);

        // Loop through values
        for (i = 0, len = value.length; i &lt; len; i++) {
            record = value[i];
            if (!record || !record.isModel) {
                record = me.findRecordByValue(record);
            }
            // record found, select it.
            if (record) {
                models.push(record);
                displayTplData.push(record.data);
                processedValue.push(record.get(me.valueField));
            }
            // record was not found, this could happen because
            // store is not loaded or they set a value not in the store
            else {
                // if valueNotFoundText is defined, display it, otherwise display nothing for this value
                if (Ext.isDefined(valueNotFoundText)) {
                    displayTplData.push(valueNotFoundText);
                }
                processedValue.push(value[i]);
            }
        }

        // Set the value of this field. If we are multiselecting, then that is an array.
        me.value = me.multiSelect ? processedValue : processedValue[0];
        if (!Ext.isDefined(me.value)) {
            me.value = null;
        }
        me.displayTplData = displayTplData; //store for getDisplayValue method
        me.lastSelection = me.valueModels = models;

        if (inputEl &amp;&amp; me.emptyText &amp;&amp; !Ext.isEmpty(value)) {
            inputEl.removeCls(me.emptyCls);
        }

        // Calculate raw value from the collection of Model data
        me.setRawValue(me.getDisplayValue());
        me.checkChange();

        if (doSelect !== false) {
            me.syncSelection();
        }
        me.applyEmptyText();

        return me;
    },

<span id='Ext-form-field-ComboBox-method-getDisplayValue'>    /**
</span>     * @private Generate the string value to be displayed in the text field for the currently stored value
     */
    getDisplayValue: function() {
        return this.displayTpl.apply(this.displayTplData);
    },

    getValue: function() {
        // If the user has not changed the raw field value since a value was selected from the list,
        // then return the structured value from the selection. If the raw field value is different
        // than what would be displayed due to selection, return that raw value.
        var me = this,
            picker = me.picker,
            rawValue = me.getRawValue(), //current value of text field
            value = me.value; //stored value from last selection or setValue() call

        if (me.getDisplayValue() !== rawValue) {
            value = rawValue;
            me.value = me.displayTplData = me.valueModels = null;
            if (picker) {
                me.ignoreSelection++;
                picker.getSelectionModel().deselectAll();
                me.ignoreSelection--;
            }
        }

        return value;
    },

    getSubmitValue: function() {
        return this.getValue();
    },

    isEqual: function(v1, v2) {
        var fromArray = Ext.Array.from,
            i, len;

        v1 = fromArray(v1);
        v2 = fromArray(v2);
        len = v1.length;

        if (len !== v2.length) {
            return false;
        }

        for(i = 0; i &lt; len; i++) {
            if (v2[i] !== v1[i]) {
                return false;
            }
        }

        return true;
    },

<span id='Ext-form-field-ComboBox-method-clearValue'>    /**
</span>     * Clears any value currently set in the ComboBox.
     */
    clearValue: function() {
        this.setValue([]);
    },

<span id='Ext-form-field-ComboBox-method-syncSelection'>    /**
</span>     * @private Synchronizes the selection in the picker to match the current value of the combobox.
     */
    syncSelection: function() {
        var me = this,
            ExtArray = Ext.Array,
            picker = me.picker,
            selection, selModel;
        if (picker) {
            // From the value, find the Models that are in the store's current data
            selection = [];
            ExtArray.forEach(me.valueModels || [], function(value) {
                if (value &amp;&amp; value.isModel &amp;&amp; me.store.indexOf(value) &gt;= 0) {
                    selection.push(value);
                }
            });

            // Update the selection to match
            me.ignoreSelection++;
            selModel = picker.getSelectionModel();
            selModel.deselectAll();
            if (selection.length) {
                selModel.select(selection);
            }
            me.ignoreSelection--;
        }
    }
});
</pre>
</body>
</html>
